﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Reflection;
using System.Diagnostics;
using System.IO;
using System.Configuration;

namespace Lyon
{
    /// <summary>
    /// 更新服务。
    /// </summary>
    static public class AutoUpdater
    {
        /// <summary>
        /// 开始更新。
        /// </summary>
        /// <param name="appName">应用程序名称</param>
        static public void Start(string appName)
        {
            Start(appName, false);
        }
        /// <summary>
        /// 开始更新。
        /// </summary>
        /// <param name="silentUpdate">静默更新</param>
        /// <param name="appName">应用程序名称</param>
        static public void Start(string appName, bool silentUpdate)
        {
            Process currentProcess = Process.GetCurrentProcess();

            Arguments arguments = new Arguments();
            arguments.ProcessID = currentProcess.Id;
            arguments.ProcessName = currentProcess.ProcessName;
            arguments.AppMainWindowTitle = appName;
            arguments.AppFileName = currentProcess.MainModule.FileName;
            arguments.SilentUpdate = silentUpdate;

            string destFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), arguments.ProcessName);
            Directory.CreateDirectory(destFolder);
            string updateApp = CopyAssembly(Assembly.GetExecutingAssembly(), destFolder);
            Process updateProcess = Process.Start(updateApp, Arguments.Serialize(arguments));
            updateProcess.WaitForExit();
        }
        static private string CopyAssembly(Assembly assembly, string destFolder)
        {
            FileInfo sourInfo = new FileInfo(assembly.Location);
            string destFile = Path.Combine(destFolder, sourInfo.Name);
            File.Copy(sourInfo.FullName, destFile, true);

            AssemblyName[] referencedAssemblies = assembly.GetReferencedAssemblies();
            foreach (AssemblyName assemblyName in referencedAssemblies)
            {
                Assembly refAss = Assembly.Load(assemblyName);
                if (NeedCopyAssembly(refAss, assembly))
                    CopyAssembly(refAss, destFolder);
            }

            return destFile;
        }
        static private bool NeedCopyAssembly(Assembly assembly, Assembly parentAssembly)
        {
            FileInfo assemblyInfo = new FileInfo(assembly.Location);
            FileInfo parentAssemblyInfo = new FileInfo(parentAssembly.Location);
            return assemblyInfo.DirectoryName == parentAssemblyInfo.DirectoryName;
        }

        /// <summary>
        /// 当前更新版本
        /// </summary>
        static public object UpdateVersion
        {
            get
            {
                return ConfigurationManager.AppSettings["UpdateVersion"];
            }
        }
        /// <summary>
        /// 当前更新地址
        /// </summary>
        static public string UpdateUrl
        {
            get
            {
                return ConfigurationManager.AppSettings["UpdateUrl"];
            }
        }
    }

    [Serializable]
    class Arguments
    {
        static IFormatter _Formatter = new BinaryFormatter();
        static public string Serialize(object obj)
        {
            if (obj == null)
            {
                return null;
            }
            using (MemoryStream stream = new MemoryStream())
            {
                _Formatter.Serialize(stream, obj);
                return Convert.ToBase64String(stream.ToArray());
            }
        }
        static public object Deserialize(string str)
        {
            if (str == null)
            {
                return null;
            }
            using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(str)))
            {
                return _Formatter.Deserialize(stream);
            }
        }

        public int ProcessID = -1;
        public string ProcessName;

        public string AppMainWindowTitle;
        public string AppFileName;

        public bool SilentUpdate;
    }
}